/** @brief Define the table cursor class.
 *
 *  tableCursor.h
 *  kangmodb
 *
 *  Created by 강모 김 on 11. 4. 24..
 *  Copyright 2011 강모소프트. All rights reserved.
 *
 */

#ifndef _KD_TABLE_CURSOR_H_
#define _KD_TABLE_CURSOR_H_ (1)

#include "kdInfra.h"
#include "key.h"
#include "data.h"
#include "table.h"


/** @brief The table cursor object that provides interfaces for searching and modifying records in a table. 
 *  A table cursor is opened by stgKangmoDB::openTableCursor, and closed by stgKangmoDB::closeTableCursor.
 */
class stgTableCursor
{	
private :
	stgTransaction * tx_;
	stgTable * table_;
	
	// TODO : Add test case for lockKey
	
	/** @brief Lock the key to get the data descriptor associated with the key.
     * Throw KD_EXCP_KEY_LOCKED in case the key is already being modified by another tranasction.
	 */
	KD_VOID lockKey( const stgKey & key, stgDataDesc ** dataDesc )
	{
		KD_TRY
		{
			stgDataDesc * d;
			bool canModify;
			
			KD_CALL( table_->getDataDesc( key, &d ));

			KD_CALL( d->tryModification( tx_, &canModify ) );
			if ( ! canModify )
				KD_THROW( KD_EXCP_KEY_LOCKED );
				
			*dataDesc = d;
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
public :
	/** @brief Initialize the table cursor with a given transaction.
	 */
	KD_VOID Initialize(stgTransaction * tx, stgTable * table)
	{
		KD_TRY
		{
			tx_ = tx;
			table_ = table;
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Destroy the table cursor 
	 */
	KD_VOID Initialize()
	{
		KD_TRY
		{
			// Nothing to do for now.
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	/** @ Seek a key to get the associated data
	 * Throw KD_EXCP_KEY_NOT_FOUND if the key is not found. 
	 */
	KD_VOID seekRecord(const stgKey & key, stgData * data )
	{
		KD_TRY
		{
			stgDataDesc * dataDesc;
			stgVersion viewVersion;
			
			KD_CALL( table_->getDataDesc( key, &dataDesc ));
			if ( dataDesc == NULL )
			{
				KD_THROW(KD_EXCP_KEY_NOT_FOUND);
			}

			viewVersion = tx_->getViewVersion();
			
			// If current tranasction is the modifying transaction, read uncomitted versions as well.
			if ( dataDesc->isModifyingTransaction(tx_) )
				viewVersion = MAX_DATA_VERSION;
			
			KD_CALL( table_->seekData( viewVersion, dataDesc, data ) );

			// TODO : Concurrency Control.
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
// TODO : Implement following functions.
/*	
	KD_VOID seekMin(stgKey * key, stgData * data );
	KD_VOID seekMax(stgKey * key, stgData * data );
	
	KD_VOID current(stgKey * key, stgData * data );
	KD_VOID next(stgKey * key, stgData * data );
	KD_VOID prev(stgKey * key, stgData * data );
*/	
	/** @brief Insert a key associating a data. Lock the key, write a log in the tx log buffer, insert the record.
	 * Throw KD_EXCP_KEY_LOCKED in case the key is already being modified by another tranasction.
	 * Throw KD_EXCP_KEY_EXISTS if the key already exists.
	 */
	KD_VOID insertRecord(const stgKey & key, const stgData & data )
	{
		KD_TRY
		{
			stgDataDesc * dataDesc;
			// stgDataVersion * dataVersion;

			KD_CALL( lockKey( key, &dataDesc) );
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	/** @brief Update the data associcated with the given key. Lock the key, write a log in the tx log buffer, update the record.
     * Throw KD_EXCP_KEY_LOCKED in case the key is already being modified by another tranasction.
	 * Throw KD_EXCP_KEY_NOT_FOUND if the key is not found. 
	 */
	KD_VOID updateRecord(const stgKey & key, const stgData & data )
	{
		KD_TRY
		{
			stgDataDesc * dataDesc;
			// stgDataVersion * dataVersion;

			KD_CALL( lockKey( key, &dataDesc) );
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	/** @brief Delete a key. Lock the key, write a log in the tx log buffer, delete the record.
     * Throw KD_EXCP_KEY_LOCKED in case the key is already being modified by another tranasction.
	 * Throw KD_EXCP_KEY_NOT_FOUND if the key is not found. 
	 */
	KD_VOID deleteRecord(const stgKey & key )
	{
		KD_TRY
		{
			stgDataDesc * dataDesc;
			// stgDataVersion * dataVersion;

			KD_CALL( lockKey( key, &dataDesc) );
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
};

#endif /* _KD_TABLE_CURSOR_H_ */